#include "console_key_reader.h"


ConsoleKeyReader::ConsoleKeyReader(){
    LinuxKeys _linux_reader;
}

ConsoleKeyReader::~ConsoleKeyReader(){}

KeyCode ConsoleKeyReader::ReadKeyCode(){
/*  
    int code = _linux_reader.GetKeyCode();
    char code_seq[3];
    while(1){
        switch(code){
            case '\x1b':
                if (_linux_reader.ReadOneChar(code_seq)==0) return KeyCode{KeyType::Esc,code};
                if (_linux_reader.ReadOneChar(code_seq+1)==0) return KeyCode{KeyType::Esc,code};

                if (code_seq[0] =='['){
                    if (code_seq[1] >= '0' && code_seq[1]<='9'){
                        if (_linux_reader.ReadOneChar(code_seq+2)==0) return KeyCode{KeyType::Esc,code};
                        if (code_seq[2] == '~'){
                            switch(code_seq[1]){
                                case '3':return KeyCode{KeyType::Delete,code_seq[1]};
                                case '5':return KeyCode{KeyType::PageUp,code_seq[1]};
                                case '6':return KeyCode{KeyType::PageDown,code_seq[1]};
                            }
                        }
                    } else {
                        switch(code_seq[1]){
                            case 'A':return KeyCode{KeyType::Up,code_seq[1]};
                            case 'B':return KeyCode{KeyType::Down,code_seq[1]};
                            case 'C':return KeyCode{KeyType::Right,code_seq[1]};
                            case 'D':return KeyCode{KeyType::Left,code_seq[1]};
                            case 'H':return KeyCode{KeyType::Home,code_seq[1]};
                            case 'F':return KeyCode{KeyType::End,code_seq[1]};
                        }
                    }
                } else if (code_seq[0]=='O'){
                    switch(code_seq[1]){
                        case 'H':return KeyCode{KeyType::Home,code_seq[1]};
                        case 'F':return KeyCode{KeyType::End,code_seq[1]};
                    }
                }
                break;
            default:
                return KeyCode{KeyType::Char,code};
        }
    }
*/
//    /*  
    int code = _linux_reader.GetKeyCode();
    if (code =='\x1b' ){ //ESC
        char c;
        int nread = _linux_reader.ReadOneChar(&c);
        if (nread ==0){
            return KeyCode{KeyType::Esc,code};
        } // use timeout to determine the ESC
        code = (int)c;
        if (code == (int)'O'){
            code = _linux_reader.GetKeyCode();
            switch(code){
                case (int)'P':
                case (int)'Q':
                case (int)'R':
                case (int)'S':{
                    return KeyCode{KeyType::F,1+code-(int)'P'};
                } break;
                default:{
                    return KeyCode{KeyType::Invalid,-1};
                } break;
            }
        } else if (code == (int)'['){
            return parse_csi();
        } else {
            return KeyCode{KeyType::Alt,code};
        } 
    } else if (code == (int)'\x7F'){
        return KeyCode{KeyType::Backspace,code};
    } else if (code == (int)'\0'){
        return KeyCode{KeyType::Null,code};
    } else if (code == (int)'\x0A'){
        return KeyCode{KeyType::Char,code}; // how to identify Enter and Ctrl+m  ??????????
    } else if (code >= (int)'\x01' && code <= (int)'\x1A'){
        return KeyCode{KeyType::Ctrl, code -1 + (int)'a'};
    } else if (code >= (int)'\x1C' && code <= (int)'\x1F'){
        return KeyCode{KeyType::Ctrl, code - 0x1C+(int)'4'};
    } else {
        return KeyCode{KeyType::Char, code};
    }
//    */
}

KeyCode ConsoleKeyReader::parse_csi(){
    int code =_linux_reader.GetKeyCode();
    switch(code){
        case '[':{
            code = _linux_reader.GetKeyCode();
            if (code >='A' && code <= 'E'){
                return KeyCode{KeyType::F,1+code-'A'};
            } else {
                return KeyCode{KeyType::Invalid,code};
            }
        }
        case 'D': return KeyCode{KeyType::Left,code};
        case 'C': return KeyCode{KeyType::Right,code};
        case 'A': return KeyCode{KeyType::Up,code};
        case 'B': return KeyCode{KeyType::Down,code};
        case 'H': return KeyCode{KeyType::Home,code};
        case 'F': return KeyCode{KeyType::End,code};
        case 'Z': return KeyCode{KeyType::BackTab,code};
        case '\n': return KeyCode{KeyType::Char,code};//use Char(13) instead of KeyType::Enter
        case 'M': return KeyCode{KeyType::Invalid,-1};//Mouse event ignored!
        case '<': return KeyCode{KeyType::Invalid,-1};//Mouse event ignored!
        default :{
            if (code >'0' && code < '9'){
                std::vector<int> vkey;
                vkey.push_back(code);
                code = _linux_reader.GetKeyCode();
                while(code <64 || code > 126){
                    vkey.push_back(code);
                    code = _linux_reader.GetKeyCode();
                }
                if (code == 'M'){
                    return KeyCode{KeyType::Invalid,-1};//Mouse event ignored!
                }
                if (code == '~'){
                    std::string str;
                    str.assign(vkey.begin(),vkey.end());
                    std::vector<std::string> vstr;
                    SplitString(str.c_str(),vstr,";");
                    if (vstr.size()==0 || vstr.size()>1){
                        return KeyCode{KeyType::Invalid,-1};
                    }
                    int realcode = std::stoi(vstr[0]);
                    switch(realcode){
                        case 1:
                        case 7: return KeyCode{KeyType::Home,realcode};
                        case 2: return KeyCode{KeyType::Insert,realcode};
                        case 3: return KeyCode{KeyType::Delete,realcode};
                        case 4: 
                        case 8: return KeyCode{KeyType::End,realcode};
                        case 5: return KeyCode{KeyType::PageUp,realcode};
                        case 6: return KeyCode{KeyType::PageDown,realcode};
                        default: {
                            if (realcode >= 11 && realcode <= 15){
                                return KeyCode{KeyType::F,realcode-10};
                            } else if (realcode >= 17 && realcode <= 21) {
                                return KeyCode{KeyType::F,realcode-11};
                            } else if (realcode >= 23 && realcode <= 24) {
                                return KeyCode{KeyType::F,realcode-12};
                            } else{
                                return KeyCode{KeyType::Invalid,realcode};
                            }

                        }
                    }
                } else{
                    return KeyCode{KeyType::Invalid,-1};
                }
            } else {
                return KeyCode{KeyType::Invalid,-1};
            }
        }
        
    }
}



/*
KeyCode ConsoleKeyReader::parse_csi(char seq1,char seq2){
    int code = _linux_reader.GetKeyCode();
    switch(code){
        case '[':{
            code = _linux_reader.GetKeyCode();
            if (code >='A' && code <= 'E'){
                return KeyCode{KeyType::F,1+code-'A'};
            } else {
                return KeyCode{KeyType::Invalid,code};
            }
        }
        case 'D': return KeyCode{KeyType::Left,code};
        case 'C': return KeyCode{KeyType::Right,code};
        case 'A': return KeyCode{KeyType::Up,code};
        case 'B': return KeyCode{KeyType::Down,code};
        case 'H': return KeyCode{KeyType::Home,code};
        case 'F': return KeyCode{KeyType::End,code};
        case 'Z': return KeyCode{KeyType::BackTab,code};
        case '\n': return KeyCode{KeyType::Char,code};//use Char(13) instead of KeyType::Enter
        case 'M': return KeyCode{KeyType::Invalid,-1};//Mouse event ignored!
        case '<': return KeyCode{KeyType::Invalid,-1};//Mouse event ignored!
        default :{
            if (code >'0' && code < '9'){
                std::vector<int> vkey;
                vkey.push_back(code);
                code = _linux_reader.GetKeyCode();
                while(code <64 || code > 126){
                    vkey.push_back(code);
                    code = _linux_reader.GetKeyCode();
                }
                if (code == 'M'){
                    return KeyCode{KeyType::Invalid,-1};//Mouse event ignored!
                }
                if (code == '~'){
                    std::string str;
                    str.assign(vkey.begin(),vkey.end());
                    std::vector<std::string> vstr;
                    SplitString(str.c_str(),vstr,";");
                    if (vstr.size()==0 || vstr.size()>1){
                        return KeyCode{KeyType::Invalid,-1};
                    }
                    int realcode = std::stoi(vstr[0]);
                    switch(realcode){
                        case 1:
                        case 7: return KeyCode{KeyType::Home,realcode};
                        case 2: return KeyCode{KeyType::Insert,realcode};
                        case 3: return KeyCode{KeyType::Delete,realcode};
                        case 4: 
                        case 8: return KeyCode{KeyType::End,realcode};
                        case 5: return KeyCode{KeyType::PageUp,realcode};
                        case 6: return KeyCode{KeyType::PageDown,realcode};
                        default: {
                            if (realcode >= 11 && realcode <= 15){
                                return KeyCode{KeyType::F,realcode-10};
                            } else if (realcode >= 17 && realcode <= 21) {
                                return KeyCode{KeyType::F,realcode-11};
                            } else if (realcode >= 23 && realcode <= 24) {
                                return KeyCode{KeyType::F,realcode-12};
                            } else{
                                return KeyCode{KeyType::Invalid,realcode};
                            }

                        }
                    }
                } else{
                    return KeyCode{KeyType::Invalid,-1};
                }
            } else {
                return KeyCode{KeyType::Invalid,-1};
            }
        }
        
    }
}
  */
void ConsoleKeyReader::SplitString(const std::string& s, std::vector<std::string>& v, const std::string& c)
{
  std::string::size_type pos1, pos2;
  pos2 = s.find(c);
  pos1 = 0;
  while(std::string::npos != pos2)
  {
    v.push_back(s.substr(pos1, pos2-pos1));

    pos1 = pos2 + c.size();
    pos2 = s.find(c, pos1);
  }
  if(pos1 != s.length())
    v.push_back(s.substr(pos1));
}

